#include"utils.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void init_table(struct table *t){

  t->head = NULL;
  t->tail = NULL;

}


int isEmpty(struct table *t){
  return (t->tail == NULL && t->head == NULL);

}


struct node *find_node(char *id, struct table *t){
  
  if (isEmpty(t))
    return NULL;
  
  struct node *noeud = t->head;
  while(noeud != NULL){
    if(strcmp(noeud->id, id) == 0)
      return noeud;
    noeud = noeud->next;
    
  }
  
  return NULL;
}

void ajouter_noeud(struct node *n ,struct table *t){
  
  if( isEmpty(t)){
      t->tail = n;
      t->head = n;
  }
  
  else{
    (t->tail)->next = n;
    t->tail=n;
  }

}

void vider_table(struct table *t){
  if(!isEmpty(t)){
    struct node *noeud = t-> head;
    struct node *tmp;
    while(noeud->next != t->tail){
      tmp = noeud;
      noeud = noeud->next;
      free(tmp);
    }
    free(t->tail);

  }
}



struct node* initialize_noeud(int code, char *id, void *content){
  struct node *noeud = (struct node *) malloc(sizeof(struct node *));
  
  noeud->code = code;
  noeud->id = malloc(sizeof(char)*strlen(id));
  strcpy(noeud->id, id);
  noeud->next = NULL;
  switch(code){

  case 0:
    noeud->content = (struct var *) content;
    break;
  case 1:
    noeud->content = (struct function *) content;
    break;
  case 2:
    noeud->content = (struct class *) content;
    break;
  default:
    noeud->content = NULL;
  }
  return noeud;
}


void add_var(char *id, char *type, struct table *t_symb, struct table *t_type, char *class){
  struct var *var = (struct var*)malloc(sizeof(struct var *));
  var->type = find_node(type, t_type);

  if(class == NULL)
    ajouter_noeud(initialize_noeud(0, id, var) ,t_symb);
  else{
    struct node *noeud = find_node(class, t_type);
    struct class *c = (struct class *)noeud->content;
    ajouter_noeud(initialize_noeud(0, id, var), c->vars);
  }
}

void add_function(char *id, int nbargs, char *typeargs[], char *typeret, char *class, struct table *t_type, struct table *t_symb){
  struct function *fct = (struct function *) malloc(sizeof(struct function *));
  
  fct->nbargs = nbargs;
  if(nbargs == 0)
    fct->typeargs = NULL;
  if(typeret == NULL)
    fct->typeretour = NULL;
  
  if(nbargs != 0){
    fct->typeargs = (struct node **) malloc(sizeof(struct node *)*nbargs);
    for(int i = 0; i< nbargs; i++){
      fct->typeargs[i] = find_node(typeargs[i], t_type);
    }
  }
  
  if(typeret != NULL)
    fct->typeretour = find_node(typeret, t_type);
  
  if(class == NULL)
    ajouter_noeud(initialize_noeud(1, id, fct), t_symb);
  else{
    struct node *noeud = find_node(class, t_type);
    struct class *c = (struct class *)noeud->content;
    ajouter_noeud(initialize_noeud(1, id, fct), c->methodes);
  }
    
}

void add_class(char *id, struct table *t_type, int typePrim, char *mere){
  struct class *classe = (struct class *) malloc(sizeof(struct class *));
  classe->vars = (struct table *) malloc(sizeof(struct table*));
  classe->methodes = (struct table *) malloc(sizeof(struct table*));
  classe->typePrim = typePrim;
  init_table(classe->vars);
  init_table(classe->methodes);
  if(mere != NULL){
    struct node *noeud = find_node(mere, t_type);
    classe->mere = noeud;
  }
  else
    classe->mere = NULL;
  ajouter_noeud(initialize_noeud(2, id, classe), t_type);
}

void debug_symb(struct table *t){
  
  struct node *noeud = t->head;
  while(noeud != NULL){
    if(noeud->code == 0){
      struct var *var = (struct var*)noeud->content;
      struct node *type = var->type;
      printf("%s de type %s\n", noeud->id, type->id);
      noeud = noeud->next;
    }
    else{
      if(noeud->code == 1){
	struct function *fct = (struct function *)noeud->content;
	if(fct->nbargs > 0){
	  printf("la focntion %s a %d arguments de type : ", noeud->id, fct->nbargs);
	  for(int i =0; i < fct->nbargs; i++){
	    printf("%s, ",fct->typeargs[i]->id);

	  }
	}
	else{
	  printf("la fonction %s n a pas d arguments ",noeud->id);
	}
	if(fct->typeretour != NULL)
	  printf("elle a une valeur de retour de type : %s \n",fct->typeretour->id);
      
	else
	  printf("et elle n a pas de valeur de retour \n");
	noeud = noeud->next;
      }
    }
  }
  
}
void debug_type(struct table *t){
  struct node *noeud = t->head;
  struct class * c;
  while(noeud != NULL){
    if(noeud->code == 2){
      printf("le type %s ", noeud->id);
      c = (struct class *) noeud->content;
      if(!isEmpty(c->vars)){
	printf(" a comme variable d instances : \n");
	debug_symb(c->vars);
      }
      if(!isEmpty(c->methodes)){
	printf(" a comme methodes : \n");
	debug_symb(c->methodes);
      }
      if(c->mere != NULL)
	printf("herite de la classe %s\n", c->mere->id);
    }
    printf("\n");
    noeud = noeud->next;
  }
} 


int check_type(char *id, char *type, struct table *t){
  struct node *noeud = find_node(id, t);
  if(noeud->code == 0){
    struct var *var = (struct var *) noeud->content;
    if(strcmp(type, var->type->id) == 0)
      return 1;
    else return 0;
  }
  else return 0;
}

int check_object_calling(char *id, char *fct, struct table *t, int nbargs, char *typeargs[]){
  struct node *noeud = find_node(id, t);
  struct var *var = (struct var *) noeud->content;
  struct class *c = (struct class *) var->type->content;
  struct node *n = find_node(fct, c->methodes);
  if(n == NULL)
    return 0;
  else{ 
    struct function *fct = (struct function *) n->content;
    if(fct->nbargs != nbargs)
      return 0;
    
    else{ 
      
      for(int i = 0; i < nbargs; i++){
	if(strcmp(typeargs[i], fct->typeargs[i]->id) != 0) 
	  return 0;
	
      }
      return 1;
    }
  }
}

int check_function_call(char *id, int nbargs, char *typeargs[], struct table *t){
  struct node *noeud = find_node(id, t);
  struct function *fct = (struct function *) noeud->content;
  if(fct->nbargs != nbargs)
    return 0;
  else{
    for(int i=0; i< nbargs; i++){
      if(strcmp(typeargs[i], fct->typeargs[i]->id) != 0)
	return 0;

    }
    return 1;
  }
}

char *return_type(char *id, struct table *t){
  struct node *noeud = find_node(id, t);
  struct var *var = (struct var *) noeud->content;
  return var->type->id;
}
/*int main(){
  
  struct table symboles;
  struct table types;
  
  init_table(&symboles);
  init_table(&types);
  add_class("INT", &types, 0);
  add_class("FLOAT", &types, 1);
  add_class("BOOL", &types, 2);
  add_class("STRING", &types, 3);
  
  add_class("Momo", &types, 4);
  char *typeargs[1];
  typeargs[0] = (char *)malloc(sizeof(char *)*strlen("INT"));
  strcpy(typeargs[0], "INT");
  
  add_function("f", 1, typeargs, "FLOAT", "Momo", &types, &symboles);
  add_function("g", 1, typeargs, "Momo", "Momo", &types, &symboles);
  add_function("f", 1, typeargs, "Momo", NULL, &types, &symboles); 
  add_var("a", "INT", &symboles, &types, NULL);
  add_var("b", "STRING", &symboles, &types, "Momo");
  add_var("momo", "Momo", &symboles, &types, NULL);
  
  debug_symb(&symboles);
  debug_type(&types);
  printf("check type a with INT true: %d\n",check_type("a", "INT", &symboles));
  printf("check type a with FLOAT flase: %d\n",check_type("a", "FLOAT", &symboles));

  printf("check type momo with INT false: %d\n",check_type("momo", "INT", &symboles));

  printf("check type momo with Momo true: %d\n",check_type("momo", "Momo", &symboles));

  printf("check calling  methode f on object momo true: %d\n",check_object_calling("momo", "f", &symboles,1,typeargs));

  printf("check calling  methode f on object momo false: %d\n",check_object_calling("momo", "function", &symboles,1,typeargs));

  printf("check calling function f true : %d\n",check_function_call("f", 1, typeargs, &symboles)); 
  return EXIT_SUCCESS;
}
*/
